package org.infoset.xml;

import java.io.*;
import java.util.*;

/**
 * This exception encapsulates XML runtime errors.  Although it does not need
 * to be caught, it has been declared where it is thrown.
 * In addition, it provides a data structure for encapsulating errors and
 * allows multiple errors to be associated with one exception.
 * @author <a href="mailto:alex@milowski.com">R. Alexander Milowski</a>
 */
public class XMLException extends Exception {
  private Location node;
  private List errors;

   /**
    * A data structure that encapsulates an error.
    */
  public static class Error {
     String message;
     Item location;
     Throwable cause;

     /** 
      * Constucts an error with a location.
      * @param message A textual message for the error.
      * @param location The node associated with the error.
      */
     public Error(String message,Item location) {
        this(message,location,null);
     }

     /**
      * Constructs an error with a location and cause.
      * @param message A textual message for the error.
      * @param location The node associated with the error.
      * @param cause An exception cause for the error.
      */
     public Error(String message,Item location,Throwable cause) {
        this.message = message;
        this.location = location;
        this.cause = cause;
     }

     /**
      * Returns the message associated with this error.
      */
     public String getMessage() {
        return message;
     }

     /**
      * Returns the location associated with this error.
      */
     public Item getLocation() {
        return location;
     }

     /**
      * Returns the cause associated with this error, if any.
      */
     public Throwable getCause() {
        return cause;
     }

     /**
      * Returns a string representation of the error.
      */
     public String toString() {
       // TODO: add xpath location
        return message;
     } 
  }

   /**
    * Constructs an exception with message and location.
    * @param detail The exception message.
    * @param node The exception location.
    */
  public XMLException(String detail, Location node) {
    super(detail);
    this.node = node;
  }
   /**
    * Constructs an exception with message.
    * @param detail The exception message.
    */
  public XMLException(String detail) {
    super(detail);
    this.node = null;
  }
   /**
    * Constructs an exception with message and cause
    * @param detail The exception message.
    * @param exception The cause of the exception
    */
  public XMLException(String detail,Throwable exception) {
    super(detail,exception);
  }
   /**
    * Constructs an exception with message, cause, and location
    * @param detail The exception message.
    * @param exception The cause of the exception
    * @param node The exception location.
    */
  public XMLException(String detail,Throwable exception, Location node) {
    super(detail,exception);
    this.node = node;
  }

   /**
    * Constructs an exception with message, cause, and location
    * @param exception The cause of the exception
    */
  public XMLException(Throwable exception) {
     super(exception);
  }

   /**
    * Constructs an exception with message, cause, and location
    * @param exception The cause of the exception
    * @param node The exception location.
    */
  public XMLException(Throwable exception, Location node) {
     super(exception);
    this.node = node;
  }

   /**
    * Constructs an exception with a list of errors.
    * @param detail The exception message.
    * @param errors The list of error instances.
    */
  public XMLException(String detail,List errors) {
    super(detail);
    this.node = null;
    this.errors = errors;
  }

   /**
    * Returns the location of the error.
    */
  public Location getLocation() {
    return node;
  }

   /**
    * Indicates that the exception has an error list.
    */
  public boolean hasErrorList() {
     return errors!=null && errors.size()!=0;
  }

   /**
    * Returns an iteration of the errors.
    */
  public Iterator getErrors() {
     return errors==null ? null : errors.iterator();
  }
  
  public void addError(String message,Item location) {
     if (errors==null) {
        errors = new ArrayList();
     }
     errors.add(new Error(message,location));
  }

  public void addError(String message,Item location,Throwable cause) {
     if (errors==null) {
        errors = new ArrayList();
     }
     errors.add(new Error(message,location,cause));
  }

   /**
    * Writes the exception to a print stream.
    */
  public void write(PrintWriter pw) 
  {
     if (getMessage()!=null) {
        pw.println(getMessage());
     }
     Throwable exception = getCause();
     if (exception!=null) { 
        if (getMessage()!=null) {
           pw.println("Caused by:");
	}
        if (exception instanceof XMLException) {
           ((XMLException)exception).write(pw);
        } else {
           exception.printStackTrace(pw);
        }
     }
     if (errors!=null) {
        for (Iterator elist=getErrors(); elist.hasNext(); ) {
           pw.println(elist.next().toString());
        }
     }
  }
}
